package top.suven.base.http.jetty;

import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import top.suven.base.http.jetty.settings.HttpMetricHelper;
import top.suven.base.http.jetty.settings.JettyServerPortSettings;
import top.suven.base.http.jetty.settings.JettyServerSettings;
import top.suven.base.util.constants.GlobalConstants;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import com.codahale.metrics.jetty9.InstrumentedQueuedThreadPool;
import org.eclipse.jetty.server.LowResourceMonitor;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;

import java.io.File;


@SpringBootApplication(exclude={DataSourceAutoConfiguration.class,
        DruidDataSourceAutoConfigure.class,KafkaAutoConfiguration.class})
@ComponentScan(basePackages={GlobalConstants.COMPONENT_SCAN_BASE_PACKAGES})

public  abstract  class AbstractServerApplication  implements WebServerFactoryCustomizer {

    protected static Logger logger = LoggerFactory.getLogger(AbstractServerApplication.class);
    @Autowired
    private JettyServerSettings jettyServer;

    @Autowired
    private JettyServerPortSettings serverPort;

    public  static int port = 0;
    protected  abstract String getPostByServiceName();

    /**
     * http 服务或RPC 服务类型;
     */
    enum  ServerPortEnumType{
        HTTP,RPC;
        static boolean isHttp(String http){
            return HTTP.name().equalsIgnoreCase(http);
        }
        static boolean isRpc(String rpc){
            return RPC.name().equalsIgnoreCase(rpc);
        }
    }

    /**
     * 应用服务启动实现方法
     * @param clazz
     * @param args
     */
    protected static void jettyStart(Class<?> clazz,String[] args){
        getPortFromArgs(args);
        SpringApplication.run(clazz, args);
    }
    @Override
    public void customize(WebServerFactory factory) {
        if(port == 0){
            port = parsePort();
        }

    }

    /**
     * 启动命令输入端口解释
     * @param args
     * @return
     */
    private static int getPortFromArgs(String[] args) {
        if (args.length > 0) {
            try {
                port = Integer.parseInt(args[0])>1000 ? Integer.parseInt(args[0]) : port;
                System.getProperties().put( "server.port", port );
            } catch (NumberFormatException ignore) {  }
        }
        return port;
    }


//    /**
//     * idea main 方法启动 加载自定义端口
//     */
//    @Bean
//    public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer(){
//        return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>() {
//            @Override
//            public void customize(ConfigurableWebServerFactory factory) {
//                factory.setPort(port);
//            }
//        };
//    }

    /**
     * 端口解决实现
     * @return
     */
    private int parsePort() {
        int port = 0;
        String rs = getPostByServiceName();
        String[] svrPort = rs.split("-",2);
        String serverName = svrPort[0];
        String postName =  svrPort[1];
        if(ServerPortEnumType.isHttp(serverName)){
            port  = serverPort.getPort(postName);
        }else if(ServerPortEnumType.isRpc(serverName)){
            port = serverPort.getRpcPort(postName);
        }
        return port;
    }

    /**
     * jetty 服务实现,Metric监控,threadPool,LowResourceMonitor
     * @return JettyEmbeddedServletContainerFactory
     */
    @Bean
    public JettyServletWebServerFactory jettyServletWebServerFactory() {
        if(port == 0){
            port = parsePort();
        }
        final JettyServletWebServerFactory factory =  new JettyServletWebServerFactory(port);
        factory.addServerCustomizers
                ( customizers ->   {
            final QueuedThreadPool threadPool = new InstrumentedQueuedThreadPool(HttpMetricHelper.getMetric());
            JettyServerSettings.JettyThreadPoolSettings jettyThreadPoolSettings  = jettyServer.getThreadPool();
            JettyServerSettings.JettyLowResourceMonitorSettings jettyResourceSettings  = jettyServer.getLowResources();

            logger.warn("JettyServerSettings.JettyThreadPoolSettings =:[{}]", jettyThreadPoolSettings.toString());
            logger.warn("JettyServerSettings.JettyLowResourceMonitorSettings =:[{}]", jettyResourceSettings.toString());

            threadPool.setMaxThreads(jettyThreadPoolSettings.getMaxThreads());
            threadPool.setMinThreads(jettyThreadPoolSettings.getMinThreads());
            threadPool.setIdleTimeout(jettyThreadPoolSettings.getIdleTimeout());
            threadPool.setDetailedDump(jettyThreadPoolSettings.isDetailedDump());
            Server server = new Server(threadPool);


            LowResourceMonitor lowResourcesMonitor = new LowResourceMonitor(server);
            lowResourcesMonitor.setPeriod(jettyResourceSettings.getPeriod());
            lowResourcesMonitor.setLowResourcesIdleTimeout(jettyResourceSettings.getIdleTimeout());
            lowResourcesMonitor.setMonitorThreads(jettyResourceSettings.isMonitorThreads());
            lowResourcesMonitor.setMaxConnections(jettyResourceSettings.getMaxConnections());
            lowResourcesMonitor.setMaxMemory(jettyResourceSettings.getMaxMemory());
            lowResourcesMonitor.setMaxLowResourcesTime(jettyResourceSettings.getMaxLowResourcesTime());
            server.addBean(lowResourcesMonitor);
        });
        factory.setDocumentRoot(new File(jettyServer.getDocumentRoot()));
        return factory;
    }
}
